// Copyright DustedPixels.com 2008. All rights reserved.

package com.dustedpixels.asm.analysis;

import java.util.List;

import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.BasicInterpreter;
import org.objectweb.asm.tree.analysis.Interpreter;
import org.objectweb.asm.tree.analysis.Value;

/**
 * Interpreter to analyze value flow.
 * 
 * <p>
 * All operations except {@code copyOperation} and {@code merge} of the same
 * values creates new instance of Value. Merging of two different values results
 * in new value.
 * 
 * <p>
 * This interpreter can be used to check, whether two values stored on stack or
 * in local slot are the same values.
 * 
 * @author micapolos@gmail.com (Michal Pociecha-Los)
 */
public final class FlowInterpreter implements Interpreter {
  private final BasicInterpreter basicInterpreter;

  public FlowInterpreter() {
    this.basicInterpreter = new BasicInterpreter();
  }
  
  public Value newValue(Type type) {
    return FlowValue.newLike(basicInterpreter.newValue(type));
  }

  public Value copyOperation(AbstractInsnNode insn, Value value) throws AnalyzerException {
    return value;
  }

  public Value newOperation(AbstractInsnNode insn) throws AnalyzerException {
    return FlowValue.newLike(basicInterpreter.newOperation(insn));
  }

  public Value unaryOperation(AbstractInsnNode insn, Value value) throws AnalyzerException {
    return FlowValue.newLike(basicInterpreter.unaryOperation(insn, value));
  }  

  public Value binaryOperation(AbstractInsnNode insn, Value value1, Value value2)
      throws AnalyzerException {
    return FlowValue.newLike(basicInterpreter.binaryOperation(insn, value1, value2));
  }

  public Value ternaryOperation(AbstractInsnNode insn, Value value1, Value value2, Value value3)
      throws AnalyzerException {
    return FlowValue.newLike(basicInterpreter.ternaryOperation(insn, value1, value2, value3));
  }

  @SuppressWarnings("unchecked")
  public Value naryOperation(AbstractInsnNode insn, List values) throws AnalyzerException {
    return FlowValue.newLike(basicInterpreter.naryOperation(insn, values));
  }

  public Value merge(Value v, Value w) {
    System.out.println("Merge: " + v + " :: " + w);
    if (v.equals(w)) {
      return v;
    } else {
      return FlowValue.merged((FlowValue) v, (FlowValue) w);
    }
  }
}
